/**
 * 示例说明
 * 
 * HelloOSS是OSS Java SDK的示例程序，您可以修改endpoint、accessKeyId、accessKeySecret、bucketName后直接运行。
 * 运行方法请参考README。
 * 
 * 本示例中的并不包括OSS Java SDK的所有功能，详细功能及使用方法，请参看“SDK手册 > Java-SDK”，
 * 链接地址是：https://help.aliyun.com/document_detail/oss/sdk/java-sdk/preface.html?spm=5176.docoss/sdk/java-sdk/。
 * 
 * 调用OSS Java SDK的方法时，抛出异常表示有错误发生；没有抛出异常表示成功执行。
 * 当错误发生时，OSS Java SDK的方法会抛出异常，异常中包括错误码、错误信息，详细请参看“SDK手册 > Java-SDK > 异常处理”，
 * 链接地址是：https://help.aliyun.com/document_detail/oss/sdk/java-sdk/exception.html?spm=5176.docoss/api-reference/error-response。
 * 
 * OSS控制台可以直观的看到您调用OSS Java SDK的结果，OSS控制台地址是：https://oss.console.aliyun.com/index#/。
 * OSS控制台使用方法请参看文档中心的“控制台用户指南”， 指南的来链接地址是：https://help.aliyun.com/document_detail/oss/getting-started/get-started.html?spm=5176.docoss/user_guide。
 * 
 * OSS的文档中心地址是：https://help.aliyun.com/document_detail/oss/user_guide/overview.html。
 * OSS Java SDK的文档地址是：https://help.aliyun.com/document_detail/oss/sdk/java-sdk/install.html?spm=5176.docoss/sdk/java-sdk。
 * 
 */

package cn.com.connected.common.config.oss;

import cn.com.connected.base.exception.BusinessException;
import cn.com.connected.base.exception.ErrorCode;
import cn.com.connected.base.utils.UUIdGenId;
import cn.com.connected.common.emnus.CacheKey;
import cn.com.connected.common.emnus.FileType;
import cn.com.connected.vo.base.AttrVO;
import cn.com.connected.vo.base.OssZipDto;
import cn.com.connected.vo.base.OssZipSonDto;
import cn.com.connected.vo.base.PolicyVO;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.imm20200930.Client;
import com.aliyun.imm20200930.models.GetTaskResponse;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.*;
import com.aliyun.sdk.service.imm20200930.AsyncClient;
import com.aliyun.sdk.service.imm20200930.models.CreateFileCompressionTaskRequest;
import com.aliyun.sdk.service.imm20200930.models.CreateFileCompressionTaskResponse;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.gson.Gson;
import darabonba.core.client.ClientOverrideConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Configuration
@Slf4j
public class OssClient {

    @Resource(name = "appOssClient")
    private OSS appOssClient;

    @Resource
    private AliyunOssProperties aliyunOssProperties;


    public String uploadOpenBucketName(String fileName,InputStream is){
            return this.upload(aliyunOssProperties.getOpenBucketName(),fileName,is);
    }

    public String upload(String bucketName,String fileName,InputStream is ){
        try {
            if (!appOssClient.doesBucketExist(bucketName)) {
                throw new BusinessException(ErrorCode.WARING,"bucket:"+bucketName+"不存在");
            }

            appOssClient.putObject(bucketName, fileName, is);
            log.info("Object：" + fileName + "存入OSS成功。");

            URL url = appOssClient.generatePresignedUrl(bucketName,fileName, new Date(System.currentTimeMillis() + 30 * 1000));
            log.info(url.toString());
            return url.getPath();
        }catch (Exception e){
            log.error(e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_FAIL,"上传失败");
        }
    }


    public String authorize(String bucketName,String fileName,int expires,String style){
        try {
            // 指定过期时间为10分钟。
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, fileName, HttpMethod.GET);
            req.setExpiration( new Date(System.nanoTime() + expires * 1000));
            if (StringUtils.isNotEmpty(style)){
                req.setProcess(style);
            }
            return appOssClient.generatePresignedUrl(req).toString();
        }catch (Exception e){
            log.error(e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_FAIL,"oss授权失败");
        }
    }


    public List<AttrVO> authorize(List<String> attrs, FileType fileType){

        List<AttrVO> attrVOS = new ArrayList<>();
        if (CollectionUtils.isEmpty(attrs)){
            return attrVOS;
        }

        //授权
        attrs.forEach(z->{
            if (z!=null){
                String primary = this.authorize(aliyunOssProperties.getOpenBucketName(),z, CacheKey.hour,null);;
                AttrVO attr = AttrVO.builder().key(z).primary(primary).build();
                attrVOS.add(attr);
            }else{
                attrVOS.add(new AttrVO());
            }
        });
        return attrVOS;
    }

    public String authorize(String attr, FileType fileType) {
        if (attr.trim().indexOf("http")==0){
            return attr;
        }

        //需要授权的
        String primary = this.authorize(aliyunOssProperties.getOpenBucketName(),attr, CacheKey.hour,null);
        return primary;
    }

    public PolicyVO createPolicy(String key) throws UnsupportedEncodingException {
        Long expireEndTime = 2*60*1000L;
        Date expiration = new Date(System.currentTimeMillis() + expireEndTime);

        PolicyConditions policyConds = new PolicyConditions();

        //限定大小
        policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);

        //精确限定文件目录
        policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, key);

        String postPolicy = appOssClient.generatePostPolicy(expiration, policyConds);
        byte[] binaryData = postPolicy.getBytes("utf-8");
        String encodedPolicy = BinaryUtil.toBase64String(binaryData);
        String postSignature = appOssClient.calculatePostSignature(postPolicy);

        return PolicyVO.builder()
                .accessId(aliyunOssProperties.getAccessKeyId())
                .policy(encodedPolicy)
                .signature(postSignature)
                .expireTime(Long.valueOf(expireEndTime / 1000L).intValue())
                .key(key)
                .uploadUrl(aliyunOssProperties.getOpenAccessHost())
                .build();
    }

//    /**
//     * 批量下载文件
//     * @param dowFile
//     * @param map
//     * @return
//     */
//    public File downloadZip(File dowFile,Map<String,String> map) throws IOException {
//        ZipOutputStream zipStream = new ZipOutputStream(new FileOutputStream(dowFile));
//        map.forEach((key,value)->{
//            String fileName = key;
//            String fileUrl = value;
//            if(StringUtils.isEmpty(fileUrl)){
//                return;
//            }
//            String fileUrlOk = addDomain(fileUrl);
//            //fileUrlOk = fileUrlOk.startsWith("/") ? fileUrl.substring(1) : fileUrl;
//            fileUrlOk = fileUrlOk.replace(aliyunOssProperties.getOpenAccessHost()+"/", "");
//            try {
//                OSSObject ossObject = appOssClient.getObject(aliyunOssProperties.getOpenBucketName(),fileUrlOk);
//                //获取文件后缀名
//                String text = fileUrl.substring(fileUrl.lastIndexOf("."));
//                InputStream inputStream = ossObject.getObjectContent();
//                byte[] buffs = new byte[1024 * 10];
//                String zipFile = fileName + text;
//                ZipEntry zipEntry = new ZipEntry(zipFile);
//                zipStream.putNextEntry(zipEntry);
//                BufferedInputStream bis = new BufferedInputStream(inputStream, 1024 * 10);
//                int read;
//                while ((read = bis.read(buffs, 0, 1024 * 10)) != -1) {
//                    zipStream.write(buffs, 0, read);
//                }
//                inputStream.close();
//                bis.close();
//                ossObject.close();
//            }catch (Exception e){
//                log.error("OSS下载ZIP失败",fileName+"-"+fileUrl);
//            }
//        });
//        zipStream.flush();
//        zipStream.close();
//        return dowFile;
//    }


    /**
     * 批量打包文件
     * @param ossZipDto
     * @return
     */
    public String newDownloadZip(OssZipDto ossZipDto){
        if(ossZipDto.getFileList().isEmpty()){
            throw new BusinessException(ErrorCode.WARING,"没有可压缩的文件");
        }
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(aliyunOssProperties.getAccessKeyId())
                .accessKeySecret(aliyunOssProperties.getAccessKeySecret())
                .build());
        AsyncClient client = AsyncClient.builder()
                .region("cn-beijing") // Region ID
                .credentialsProvider(provider)
                .overrideConfiguration(ClientOverrideConfiguration.create().setEndpointOverride("imm.cn-beijing.aliyuncs.com"))
                .build();
        List<CreateFileCompressionTaskRequest.Sources> list = new ArrayList<>();
        for (OssZipSonDto o : ossZipDto.getFileList()) {
            String fileExtension = o.getFileUrl().substring(o.getFileUrl().lastIndexOf("."));
            CreateFileCompressionTaskRequest.Sources sources = CreateFileCompressionTaskRequest.Sources.builder()
                    .URI("oss://ngkcoss"+o.getFileUrl())
                    .alias("/"+o.getNewName()+fileExtension)
                    .build();
            list.add(sources);
        }
        CreateFileCompressionTaskRequest createFileCompressionTaskRequest = CreateFileCompressionTaskRequest.builder()
                .projectName("zipPack")
                .targetURI("oss://ngkcoss/zip/"+ossZipDto.getZipName()+".zip")
                .compressedFormat("zip")
                .sources(list)
                .build();
        // Asynchronously get the return value of the API request
        CompletableFuture<CreateFileCompressionTaskResponse> response = client.createFileCompressionTask(createFileCompressionTaskRequest);
        // Synchronously get the return value of the API request
        try {
            CreateFileCompressionTaskResponse resp = response.get();
            System.out.println(new Gson().toJson(resp));
            client.close();
            String taskId = resp.getBody().getTaskId();
            for (int i = 0; i < 20; i++) {
                String status = selectZip(taskId);
                System.out.println("status:"+status);
                if(status.equals("Succeeded")){
                    return aliyunOssProperties.getOpenAccessHost()+"/zip/"+ossZipDto.getZipName()+".zip";
                }else if(status.equals("Failed")) {
                    throw new BusinessException(ErrorCode.WARING,"ZIP压缩失败");
                }
                Thread.sleep(1000);
            }
            throw new BusinessException(ErrorCode.WARING,"ZIP压缩超时");
        }catch (Exception e){
            client.close();
            e.printStackTrace();
            throw new BusinessException(ErrorCode.WARING,"ZIP压缩失败");
        }

    }

    private String selectZip (String taskId) throws Exception {
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                .setAccessKeyId(aliyunOssProperties.getAccessKeyId())
                .setAccessKeySecret(aliyunOssProperties.getAccessKeySecret());
        config.endpoint = "imm.cn-beijing.aliyuncs.com";
        com.aliyun.imm20200930.Client client = new Client(config);
        com.aliyun.imm20200930.models.GetTaskRequest getTaskRequest = new com.aliyun.imm20200930.models.GetTaskRequest()
                .setProjectName("zipPack")
                .setTaskType("FileCompression")
                .setTaskId(taskId);
        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
        // 复制代码运行请自行打印 API 的返回值
        GetTaskResponse response = client.getTaskWithOptions(getTaskRequest, runtime);
        return response.getBody().getStatus();
    }



    /**
     * 读取图片
     * @param fileUrl
     * @return
     */
    public InputStream readImg(String fileUrl){
        try {
            String fileUrlOk = fileUrl.startsWith("/") ? fileUrl.substring(1) : fileUrl;
            OSSObject ossObject = appOssClient.getObject(aliyunOssProperties.getOpenBucketName(),fileUrlOk);
            return ossObject.getObjectContent();
        }catch (Exception e){
            return null;
        }
    }

    /**
     * 视频转码任务
     * @param fileUrl
     */
    public String videoChange(String fileUrl){
        if(!fileUrl.toLowerCase().endsWith(".mp4")){
            return "";
        }
        //指定原视频文件名称
        String sourceKey = fileUrl.startsWith("/") ? fileUrl.substring(1) : fileUrl;
        //指定处理后视频文件名称
        String targetKey = FileType.DYNAMIC_COMMENT_AUDIO.getDefaultPostfix()+"video/"+ UUIdGenId.UUID() +".mp4";
        try {
            // 构建视频处理样式字符串以及视频转码处理参数。
            String style = String.format("video/convert,f_mp4,vcodec_h264");
            // 构建异步处理指令
            String bucketEncoded = Base64.getUrlEncoder().withoutPadding().encodeToString(aliyunOssProperties.getOpenBucketName().getBytes());
            String targetEncoded = Base64.getUrlEncoder().withoutPadding().encodeToString(targetKey.getBytes());
            String process = String.format("%s|sys/saveas,b_%s,o_%s", style, bucketEncoded, targetEncoded);
            //创建AsyncProcessObjectRequest对象。
            AsyncProcessObjectRequest request = new AsyncProcessObjectRequest(aliyunOssProperties.getOpenBucketName(), sourceKey, process);
            //执行异步处理任务。
            AsyncProcessObjectResult response = appOssClient.asyncProcessObject(request);
            System.out.println("EventId: " + response.getEventId());
            System.out.println("RequestId: " + response.getRequestId());
            System.out.println("TaskId: " + response.getTaskId());
            return "/" + targetKey;
        } catch (Exception e){
            log.error("OSS视频转码失败",sourceKey);
            return "";
        }
    }
}
